#!/usr/bin/env python
# coding: utf-8

import pandas as pd
import numpy as np
import os
import glob
from tqdm import tqdm
import warnings
from datetime import datetime, timedelta
import multiprocessing as mp
from hmmlearn import hmm
from sklearn.preprocessing import StandardScaler
import joblib # 用于加载任务二的模型（如果需要）
import sys
import time
import matplotlib.pyplot as plt
import seaborn as sns

# --- Matplotlib and Seaborn Settings (English Labels) ---
# plt.rcParams['font.sans-serif'] = ['SimHei'] # Commented out
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style="whitegrid")

# --- 配置参数 ---
# 数据路径 (需要与 task2 一致)
# BASE_DATA_DIR = r"C:\Users\ASUS\Desktop\B题-全部数据" #<--- Windows 路径示例
BASE_DATA_DIR = "/share/home/u2201230214/GPR/B" #<--- 更新为 Linux 路径
DATA_DIR_TEST = os.path.join(BASE_DATA_DIR, "附件2")
METADATA_TEST = os.path.join(DATA_DIR_TEST, "Metadata2.csv")

# 任务二结果路径 (用于读取 MET 预测)
# TASK2_PREDICTIONS_DIR = "result_2" #<--- 相对路径示例
TASK2_PREDICTIONS_DIR = "/share/home/u2201230214/GPR/result_2" #<--- 更新为 Linux 绝对路径

# 输出路径 (符合比赛要求)
# 先定义
RESULTS_DIR = "results_task3" #<--- 新增：任务三结果目录
# 后使用
OUTPUT_SUMMARY_FILE = os.path.join(RESULTS_DIR, "result_3_summary.xlsx") 
FIGURES_SAVE_PATH = os.path.join(RESULTS_DIR, "figures") #<--- 新增：图表保存路径

# 睡眠定义
MET_SLEEP_THRESHOLD = 1.0 # MET 值低于此阈值为睡眠

# HMM 参数
N_SLEEP_MODES = 3 # 定义睡眠模式数量 (HMM 状态数)
N_HMM_ITERATIONS = 100 # HMM 训练迭代次数
RANDOM_STATE = 42 # 保证结果可复现
SLEEP_MODE_NAMES = [f"Mode {i+1}" for i in range(N_SLEEP_MODES)] # 模式名称列表

# 滑动窗口参数 (需要与 task2 生成 MET 预测时一致)
WINDOW_SIZE_SEC = 6
STEP_SEC = 3
SAMPLING_RATE_HZ = 10 # 假设采样率

# 并行处理进程数
NUM_PROCESSES = mp.cpu_count()
NUM_SUBJECTS_FOR_DETAIL_PLOTS = 3 # 为多少个受试者绘制详细图表

warnings.filterwarnings('ignore', category=DeprecationWarning)
warnings.filterwarnings('ignore', category=FutureWarning)

# --- 辅助函数 (部分可复用 task2) ---

def parse_time(df):
    """统一处理时间戳列 (加速度数据文件)"""
    if 'time' not in df.columns:
        raise ValueError("数据文件缺少 'time' 列")
    try:
        df['datetime'] = pd.to_datetime(df['time'], format="%Y-%m-%d %H:%M:%S.%f", errors='coerce')
    except Exception:
        try:
            df['datetime'] = pd.to_datetime(df['time'], errors='coerce')
        except Exception as e2:
             print(f"解析时间戳失败: {e2}. 检查 'time' 列格式。")
             df['datetime'] = pd.NaT
    original_len = len(df)
    df = df.dropna(subset=['datetime'])
    dropped_rows = original_len - len(df)
    # if dropped_rows > 0: # 减少冗余输出
    #     print(f"警告: 因时间戳解析失败，删除了 {dropped_rows} 行数据。")
    if not df.empty:
        df = df.sort_values('datetime')
    return df

def calculate_magnitude(df):
    """计算加速度幅值"""
    df['magnitude'] = np.sqrt(df['x']**2 + df['y']**2 + df['z']**2)
    return df

def extract_sleep_window_features(window_data):
    """为单个睡眠窗口提取特征 (用于 HMM)"""
    if window_data.empty or len(window_data) <= 1:
        return None

    features = {}
    # 主要特征：加速度幅值的均值和标准差
    mag_series = window_data['magnitude'].dropna()
    if len(mag_series) > 1:
        features['mag_mean'] = mag_series.mean()
        features['mag_std'] = mag_series.std(ddof=0)
    else:
        features['mag_mean'] = np.nan
        features['mag_std'] = np.nan

    # 填充NaN (如果某窗口无法计算特征)
    for k, v in features.items():
        if pd.isna(v):
            features[k] = 0.0

    # 返回特征向量 (HMM 需要数值输入)
    return np.array([features['mag_mean'], features['mag_std']])

# --- 绘图函数 (全部使用英文标签) --- #

def plot_hypnogram(timestamps, modes, subject_id, save_path=FIGURES_SAVE_PATH):
    """Plot Hypnogram for a single subject (English)."""
    os.makedirs(save_path, exist_ok=True)
    if not timestamps or len(modes) == 0:
        print(f"Info: Subject {subject_id} has no valid sleep data for hypnogram.")
        return

    plt.figure(figsize=(18, 4))
    plt.step(timestamps, modes, where='post')
    plt.yticks(np.arange(N_SLEEP_MODES), SLEEP_MODE_NAMES)
    plt.xlabel("Time")
    plt.ylabel("Predicted Sleep Mode")
    plt.title(f"Subject {subject_id} Hypnogram")
    plt.grid(axis='x', linestyle='--', alpha=0.6)
    plt.tight_layout()
    plt.xticks(rotation=30, ha='right')
    try:
        plt.gca().xaxis.set_major_formatter(plt.matplotlib.dates.DateFormatter('%H:%M'))
        plt.gca().xaxis.set_major_locator(plt.matplotlib.dates.HourLocator(interval=1))
    except Exception as e:
        print(f"Error formatting time axis: {e}")
    plt.savefig(os.path.join(save_path, f"hypnogram_{subject_id}.png"), dpi=300, bbox_inches='tight')
    plt.close()

def plot_feature_distribution(all_features_df, save_path=FIGURES_SAVE_PATH):
    """Plot feature distribution per sleep mode (English)."""
    os.makedirs(save_path, exist_ok=True)
    if all_features_df.empty:
        print("Info: No feature data available for distribution plot.")
        return

    # feature_cols = ['幅值均值', '幅值标准差'] # Original Chinese
    # Correct the mapping: {original_column_name: new_display_name}
    feature_cols_map = {'mag_mean': 'Magnitude Mean', 'mag_std': 'Magnitude Std Dev'}
    plot_df = all_features_df.rename(columns=feature_cols_map)
    feature_cols_eng = list(feature_cols_map.values()) # Use the new display names for iteration

    n_features = len(feature_cols_eng)
    plt.figure(figsize=(10, 5 * n_features))
    for i, feature in enumerate(feature_cols_eng):
        plt.subplot(n_features, 1, i + 1)
        # Use the English 'Sleep Mode' column created during aggregation
        sns.violinplot(x='Sleep Mode', y=feature, data=plot_df, palette="viridis", order=SLEEP_MODE_NAMES)
        plt.title(f"Distribution of {feature} across Sleep Modes")
        plt.xlabel("Predicted Sleep Mode")
        plt.ylabel(feature)
    plt.tight_layout()
    plt.savefig(os.path.join(save_path, f"feature_distribution_by_mode.png"), dpi=300, bbox_inches='tight')
    plt.close()

def plot_mode_proportions(summary_df, save_path=FIGURES_SAVE_PATH):
    """Plot average sleep mode proportions across all subjects (English)."""
    os.makedirs(save_path, exist_ok=True)
    if summary_df.empty:
        print("Info: No summary data available for mode proportion plot.")
        return

    # mode_cols = [f'睡眠模式{i+1}总时长（分钟）' for i in range(N_SLEEP_MODES)] # Original Chinese
    mode_cols_map = {f'Mode {i+1} Duration (min)': f'睡眠模式{i+1}总时长（分钟）' for i in range(N_SLEEP_MODES)}
    plot_df = summary_df.rename(columns=mode_cols_map)
    mode_cols_eng = list(mode_cols_map.keys())

    # Check if columns exist after renaming
    mode_cols_present = [col for col in mode_cols_eng if col in plot_df.columns]
    if not mode_cols_present:
         print("Info: Mode duration columns not found in summary data.")
         return

    # Calculate mean proportions
    total_duration_min = plot_df[mode_cols_present].sum(axis=1)
    # Handle division by zero if total duration is 0 for a subject
    proportions = plot_df[mode_cols_present].apply(lambda x: x / total_duration_min.loc[x.index] * 100 if total_duration_min.loc[x.index] > 0 else 0, axis=1)
    mean_proportions = proportions.mean()
    # Ensure correct labels match the order
    mean_proportions.index = [name.replace(' Duration (min)','') for name in mean_proportions.index]

    plt.figure(figsize=(8, 6))
    mean_proportions.plot(kind='bar', color=sns.color_palette("viridis", len(mean_proportions)))
    plt.title("Average Sleep Mode Duration Proportion")
    plt.xlabel("Predicted Sleep Mode")
    plt.ylabel("Average Duration Proportion (%)")
    plt.xticks(rotation=0)
    plt.ylim(0, 100)
    plt.grid(axis='y', linestyle='--', alpha=0.6)
    plt.tight_layout()
    plt.savefig(os.path.join(save_path, f"overall_sleep_mode_proportions.png"), dpi=300, bbox_inches='tight')
    plt.close()

def plot_transition_matrix(hmm_model, subject_id, save_path=FIGURES_SAVE_PATH):
    """Plot HMM state transition matrix heatmap (English)."""
    os.makedirs(save_path, exist_ok=True)
    if hmm_model is None or not hasattr(hmm_model, 'transmat_'):
        print(f"Info: Subject {subject_id} has no valid HMM model for transition matrix.")
        return

    transmat = hmm_model.transmat_
    plt.figure(figsize=(7, 6))
    sns.heatmap(transmat, annot=True, fmt=".2f", cmap="viridis",
                xticklabels=SLEEP_MODE_NAMES, yticklabels=SLEEP_MODE_NAMES)
    plt.title(f"Subject {subject_id} Sleep Mode Transition Probabilities")
    plt.xlabel("To Mode")
    plt.ylabel("From Mode")
    plt.tight_layout()
    plt.savefig(os.path.join(save_path, f"transition_matrix_{subject_id}.png"), dpi=300, bbox_inches='tight')
    plt.close()

# --- NEW Plotting Functions --- #

def plot_total_sleep_duration_histogram(summary_df, save_path=FIGURES_SAVE_PATH):
    """Plot histogram of total sleep duration across subjects (English)."""
    os.makedirs(save_path, exist_ok=True)
    # col_name = '睡眠总时长（小时）' # Original Chinese
    col_name = 'Total Sleep Duration (hours)'
    if summary_df.empty or '睡眠总时长（小时）' not in summary_df.columns:
        print(f"Info: No data or missing column for total sleep duration histogram.")
        return

    plot_df = summary_df.rename(columns={'睡眠总时长（小时）': col_name})

    plt.figure(figsize=(10, 6))
    sns.histplot(plot_df[col_name], kde=True, bins=15)
    plt.title('Distribution of Total Sleep Duration per Subject')
    plt.xlabel(col_name)
    plt.ylabel('Number of Subjects')
    plt.grid(axis='y', linestyle='--', alpha=0.7)
    plt.savefig(os.path.join(save_path, "total_sleep_duration_histogram.png"), dpi=300, bbox_inches='tight')
    plt.close()

def plot_mode_duration_boxplots(summary_df, save_path=FIGURES_SAVE_PATH):
    """Plot boxplots of duration for each sleep mode across subjects (English)."""
    os.makedirs(save_path, exist_ok=True)
    if summary_df.empty:
        print("Info: No summary data for mode duration boxplots.")
        return

    # mode_cols_orig = [f'睡眠模式{i+1}总时长（分钟）' for i in range(N_SLEEP_MODES)] # Original Chinese
    mode_cols_map = {f'Mode {i+1} Duration (min)': f'睡眠模式{i+1}总时长（分钟）' for i in range(N_SLEEP_MODES)}
    plot_df = summary_df.rename(columns=mode_cols_map)
    mode_cols_eng = list(mode_cols_map.keys())
    mode_cols_present = [col for col in mode_cols_eng if col in plot_df.columns]

    if not mode_cols_present:
        print("Info: Mode duration columns not found for boxplots.")
        return

    # Melt the DataFrame for seaborn boxplot
    df_melted = plot_df.melt(id_vars=['志愿者ID'], value_vars=mode_cols_present,
                             var_name='Sleep Mode', value_name='Duration (minutes)')
    # Clean up mode names for display
    df_melted['Sleep Mode'] = df_melted['Sleep Mode'].str.replace(' Duration \(min\)','')

    plt.figure(figsize=(10, 6))
    sns.boxplot(x='Sleep Mode', y='Duration (minutes)', data=df_melted, palette='viridis', order=SLEEP_MODE_NAMES)
    plt.title('Distribution of Time Spent in Each Sleep Mode')
    plt.xlabel('Predicted Sleep Mode')
    plt.ylabel('Duration (minutes)')
    plt.xticks(rotation=0)
    plt.tight_layout()
    plt.savefig(os.path.join(save_path, "sleep_mode_duration_boxplots.png"), dpi=300, bbox_inches='tight')
    plt.close()

def plot_feature_scatter_by_mode(all_features_df, save_path=FIGURES_SAVE_PATH):
    """Plot scatter of HMM features colored by predicted mode (English)."""
    os.makedirs(save_path, exist_ok=True)
    if all_features_df.empty or 'mag_mean' not in all_features_df.columns or 'mag_std' not in all_features_df.columns:
        print("Info: Missing feature data for scatter plot.")
        return

    plt.figure(figsize=(10, 8))
    sns.scatterplot(data=all_features_df,
                    x='mag_mean', y='mag_std',
                    hue='Sleep Mode', # Use the English column name
                    palette='viridis', alpha=0.6,
                    hue_order=SLEEP_MODE_NAMES) # Ensure consistent color order
    plt.title('HMM Features (Magnitude Mean vs Std Dev) by Predicted Sleep Mode')
    plt.xlabel('Magnitude Mean')
    plt.ylabel('Magnitude Standard Deviation')
    plt.legend(title='Predicted Mode')
    plt.grid(True)
    plt.tight_layout()
    plt.savefig(os.path.join(save_path, "feature_scatter_by_mode.png"), dpi=300, bbox_inches='tight')
    plt.close()

# --- 主处理函数 ---

def process_subject_sleep_data(args):
    """处理单个受试者数据，识别睡眠模式并计算时长，返回统计和绘图数据"""
    subject_pid_str = args['subject_id'] # e.g., 'P101'
    subject_tid_str = f"T{subject_pid_str[1:]}" # e.g., 'T101' (用于输出)
    subject_numeric_id = subject_pid_str[1:] # e.g., '101'

    # 1. 加载该受试者的 MET 预测结果
    met_pred_file = os.path.join(TASK2_PREDICTIONS_DIR, f"{subject_tid_str}_predictions.csv")
    if not os.path.exists(met_pred_file):
        print(f"警告: 找不到受试者 {subject_tid_str} 的 MET 预测文件 {met_pred_file}，跳过任务三处理。")
        return None
    try:
        met_preds_df = pd.read_csv(met_pred_file)
        met_preds_df['timestamp'] = pd.to_datetime(met_preds_df['timestamp'])
        met_preds_df = met_preds_df.sort_values('timestamp')
    except Exception as e:
        print(f"错误: 读取或处理 MET 预测文件 {met_pred_file} 失败: {e}")
        return None

    # 2. 识别睡眠窗口 (基于 MET 预测)
    sleep_windows_met = met_preds_df[met_preds_df['predicted_met'] < MET_SLEEP_THRESHOLD]
    if sleep_windows_met.empty:
        print(f"信息: 受试者 {subject_tid_str} 未检测到睡眠时段 (MET < {MET_SLEEP_THRESHOLD})。")
        # 即使没有睡眠，也要返回空结果以填充 Excel
        sleep_stats = {'志愿者ID': subject_tid_str, '睡眠总时长（小时）': 0.0}
        for i in range(N_SLEEP_MODES):
            sleep_stats[f'睡眠模式{i+1}总时长（分钟）'] = 0.0
        return {"stats": sleep_stats, "plot_data": None}

    # 3. 加载原始加速度数据以提取睡眠特征
    accel_data_file = os.path.join(DATA_DIR_TEST, f"P{subject_numeric_id}.csv")
    if not os.path.exists(accel_data_file):
        print(f"警告: 找不到受试者 {subject_pid_str} 的原始加速度文件 {accel_data_file}，无法提取睡眠特征，跳过。")
        return None
    try:
        accel_df = pd.read_csv(accel_data_file)
        accel_df = parse_time(accel_df)
        if accel_df.empty: return None # 解析失败
        accel_df = calculate_magnitude(accel_df)
    except Exception as e:
        print(f"错误: 读取或处理加速度文件 {accel_data_file} 失败: {e}")
        return None

    # 4. 为每个睡眠窗口提取特征
    sleep_features_list = []
    valid_sleep_timestamps = []
    # 遍历 MET 预测识别出的睡眠窗口时间戳
    for sleep_window_start in sleep_windows_met['timestamp']:
        # 确定该窗口在原始加速度数据中的时间范围
        window_end = sleep_window_start + timedelta(seconds=WINDOW_SIZE_SEC)
        # 从原始数据中筛选出对应窗口的数据
        window_accel_data = accel_df[(accel_df['datetime'] >= sleep_window_start) & (accel_df['datetime'] < window_end)]

        if not window_accel_data.empty:
            features = extract_sleep_window_features(window_accel_data)
            if features is not None:
                sleep_features_list.append(features)
                valid_sleep_timestamps.append(sleep_window_start)

    if not sleep_features_list:
        print(f"警告: 受试者 {subject_tid_str} 的睡眠时段内未能提取有效特征。")
        # 返回空结果
        sleep_stats = {'志愿者ID': subject_tid_str, '睡眠总时长（小时）': 0.0}
        for i in range(N_SLEEP_MODES):
            sleep_stats[f'睡眠模式{i+1}总时长（分钟）'] = 0.0
        return {"stats": sleep_stats, "plot_data": None}

    # 将特征列表转换为 NumPy 数组
    sleep_features_array = np.array(sleep_features_list)

    # 5. 特征标准化 (HMM 对特征尺度敏感)
    scaler = StandardScaler()
    scaled_features = scaler.fit_transform(sleep_features_array)

    # 6. 训练 HMM 并预测睡眠模式
    hmm_model = None
    predicted_modes = []
    try:
        # 使用高斯发射的 HMM
        hmm_model = hmm.GaussianHMM(n_components=N_SLEEP_MODES, covariance_type="diag",
                                    n_iter=N_HMM_ITERATIONS, random_state=RANDOM_STATE)
        hmm_model.fit(scaled_features)
        # 预测每个窗口的睡眠模式 (隐藏状态)
        predicted_modes = hmm_model.predict(scaled_features)
    except Exception as e:
        print(f"错误: 为受试者 {subject_tid_str} 训练或预测 HMM 时失败: {e}")
        print("这可能发生在睡眠数据过少或特征值非常单一的情况下。将跳过此受试者。")
        # 返回空结果
        sleep_stats = {'志愿者ID': subject_tid_str, '睡眠总时长（小时）': 0.0}
        for i in range(N_SLEEP_MODES):
            sleep_stats[f'睡眠模式{i+1}总时长（分钟）'] = 0.0
        return {"stats": sleep_stats, "plot_data": None}

    # 7. 计算总睡眠时长和各模式时长
    total_sleep_windows = len(predicted_modes)
    # 总睡眠时长 = 睡眠窗口数 * 窗口步长 (秒) / 3600 (转小时)
    # 这里假设MET预测文件中的每个时间戳代表一个以 STEP_SEC 推进的窗口
    total_sleep_duration_hours = total_sleep_windows * STEP_SEC / 3600

    sleep_stats = {
        '志愿者ID': subject_tid_str,
        '睡眠总时长（小时）': total_sleep_duration_hours
    }

    # 计算每个模式的时长 (分钟)
    mode_counts = pd.Series(predicted_modes).value_counts()
    for i in range(N_SLEEP_MODES):
        mode_duration_minutes = mode_counts.get(i, 0) * STEP_SEC / 60
        sleep_stats[f'睡眠模式{i+1}总时长（分钟）'] = mode_duration_minutes

    # 准备绘图数据
    plot_data = {
        "subject_id": subject_tid_str,
        "timestamps": valid_sleep_timestamps,
        "modes": predicted_modes,
        "features": sleep_features_array, # 未标准化的特征用于绘图解释
        "hmm_model": hmm_model # 传递模型用于绘制转换矩阵
    }

    return {"stats": sleep_stats, "plot_data": plot_data}

# --- 主函数 ---
def main():
    start_overall = time.time()
    os.makedirs(RESULTS_DIR, exist_ok=True)
    os.makedirs(FIGURES_SAVE_PATH, exist_ok=True)

    # --- 1. 加载测试集元数据 ---
    print("Loading test metadata...")
    try:
        test_metadata = pd.read_csv(METADATA_TEST)
        if 'pid' not in test_metadata.columns:
            raise ValueError(f"测试元数据 {METADATA_TEST} 缺少 'pid' 列。")
        print(f"测试集元数据: {len(test_metadata)} 条记录")
    except FileNotFoundError as e:
        print(f"错误: 无法找到测试元数据文件 {e.filename}。请检查路径配置: {DATA_DIR_TEST}")
        sys.exit(1)
    except ValueError as e:
         print(f"错误: 测试元数据文件格式不正确。{e}")
         sys.exit(1)

    # --- 2. 准备并行处理任务 --- #
    print(f"\nStarting parallel processing for {len(test_metadata)} subjects ({NUM_PROCESSES} processes)...")
    tasks = []
    for _, row in test_metadata.iterrows():
        tasks.append({'subject_id': row['pid']}) # Pass 'Pxxx' ID

    # --- 3. 并行处理 --- #
    all_sleep_stats = []
    all_plot_data_list = []
    successful_subject_plot_data = []

    with mp.Pool(processes=NUM_PROCESSES) as pool:
        for result in tqdm(pool.imap_unordered(process_subject_sleep_data, tasks), total=len(tasks), desc="Analyzing Sleep Modes"):
            if result is not None:
                all_sleep_stats.append(result["stats"])
                if result["plot_data"] is not None:
                    all_plot_data_list.append(result["plot_data"])
                    if len(successful_subject_plot_data) < NUM_SUBJECTS_FOR_DETAIL_PLOTS:
                         successful_subject_plot_data.append(result["plot_data"])

    if not all_sleep_stats:
        print("\nError: Failed to process sleep data for any subject. Cannot generate results.")
        sys.exit(1)

    # --- 4. 生成汇总统计文件 (result_3.xlsx) --- #
    print(f"\nGenerating summary statistics file {OUTPUT_SUMMARY_FILE}...")
    summary_df = pd.DataFrame(all_sleep_stats)

    # 确保列顺序符合比赛要求表3
    required_columns = ['志愿者ID', '睡眠总时长（小时）'] + [f'睡眠模式{i+1}总时长（分钟）' for i in range(N_SLEEP_MODES)]
    # 使用实际存在的列，并按要求排序
    ordered_columns = [col for col in required_columns if col in summary_df.columns]
    # 添加其他可能存在的列（虽然不应该有）
    for col in summary_df.columns:
        if col not in ordered_columns:
            ordered_columns.append(col)
    summary_df = summary_df[ordered_columns]

    # 填充处理失败或无睡眠的受试者的 NaN 值为 0
    summary_df = summary_df.fillna(0)

    # 保留4位小数
    float_columns = summary_df.select_dtypes(include=[np.number]).columns.drop('志愿者ID', errors='ignore') # Exclude ID if numeric
    summary_df[float_columns] = summary_df[float_columns].round(4)

    try:
        summary_df.to_excel(OUTPUT_SUMMARY_FILE, index=False, engine='openpyxl')
        print(f"Sleep analysis summary saved to {OUTPUT_SUMMARY_FILE}")
    except Exception as e_excel:
        print(f"Error saving summary Excel file: {e_excel}")
        # Add CSV fallback if needed

    # --- 5. 生成图表 --- #
    print("\nGenerating analysis plots...")

    # 5.1 Detailed plots for representative subjects
    print(f"Generating detailed plots for first {len(successful_subject_plot_data)} successfully processed subjects...")
    for plot_data in successful_subject_plot_data:
        plot_hypnogram(plot_data["timestamps"], plot_data["modes"], plot_data["subject_id"])
        plot_transition_matrix(plot_data["hmm_model"], plot_data["subject_id"])

    # 5.2 Prepare data for overall plots
    all_features_for_plotting_df = pd.DataFrame()
    if all_plot_data_list:
        print("Aggregating feature data for overall plots...")
        combined_features_list = []
        for plot_data in all_plot_data_list:
            # Use original feature names here, mapping happens in plot functions
            subject_features = pd.DataFrame(plot_data["features"], columns=['mag_mean', 'mag_std'])
            subject_features['Sleep Mode'] = [SLEEP_MODE_NAMES[m] for m in plot_data["modes"]] # Use English mode names
            subject_features['Subject ID'] = plot_data["subject_id"] # Use Txxx ID
            combined_features_list.append(subject_features)

        if combined_features_list:
            all_features_for_plotting_df = pd.concat(combined_features_list, ignore_index=True)
            # Plot B: Feature Distribution
            plot_feature_distribution(all_features_for_plotting_df)
            # Plot F: Feature Scatter
            plot_feature_scatter_by_mode(all_features_for_plotting_df)
        else:
            print("Info: No data available for feature distribution/scatter plots.")
    else:
        print("Info: No plot data collected, skipping feature distribution/scatter plots.")

    # 5.3 Overall plots based on summary_df
    if not summary_df.empty:
        # Plot C: Mode Proportions Bar
        plot_mode_proportions(summary_df)
        # Plot D: Total Duration Histogram
        plot_total_sleep_duration_histogram(summary_df)
        # Plot E: Mode Duration Boxplots
        plot_mode_duration_boxplots(summary_df)
    else:
        print("Info: Summary data is empty, skipping overall proportion/duration plots.")

    print(f"Plots saved to {FIGURES_SAVE_PATH}")
    # --- 图表生成结束 ---

    end_overall = time.time()
    print(f"\n--- Task 3 processing completed (including plots) --- Total duration: {end_overall - start_overall:.2f} seconds ---")

# --- 启动进入点 ---
if __name__ == "__main__":
    try:
        print(f"=== 任务三脚本启动时间: {datetime.now()} ===")
        sys.stdout.flush()

        # Windows下多进程支持
        if sys.platform == 'win32':
            mp.freeze_support()

        main()

        print(f"=== 程序正常结束时间: {datetime.now()} ===")
        sys.stdout.flush()
    except Exception as e:
        print("\n=== 程序异常终止 ===")
        print(f"错误信息: {str(e)}")
        import traceback
        print(traceback.format_exc())
        sys.stdout.flush()
